package common

import akka.actor.ActorSystem
//import akka.http.javadsl.model.StatusCodes
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.marshalling.ToResponseMarshallable
import akka.http.scaladsl.model._
import akka.http.scaladsl.server
import akka.http.scaladsl.server.Route
import akka.stream.Materializer
import akka.util.Timeout
import common.ApiResponse._
import helpers._
import play.api.libs.json.Format

import scala.concurrent._
import scala.reflect.ClassTag
import scala.util._

object RoutesDefinition {

  val NotFound: ApiResponse[String] =
    Error[String](StatusCodes.NotFound.intValue, error = StatusCodes.NotFound.defaultMessage)

  val Unexpected: ApiResponse[String] = Error[String](
    StatusCodes.InternalServerError.intValue,
    error = StatusCodes.InternalServerError.defaultMessage
  )
}

trait RoutesDefinition extends server.Directives with ExtendRequestDirectives with JsonProtocol with Logging {

  import RoutesDefinition._

  import concurrent.duration._

  implicit val endpointTimeout: Timeout = Timeout(10 seconds)

  /**
   * The implicit system to use for building routes
   */
  implicit val system: ActorSystem

  /**
   * The implicit materializer to use for routes
   */
  implicit val materializer: Materializer

  /**
   * The implicit execution context to use for routes
   */
  implicit val ec: ExecutionContext = system.dispatcher

  /**
   * Returns the routes defined for this endpoint
   */
  def routes: Route

  /**
   * Uses service to get a result and then inspects that result to complete the request
   *
   * @return a completed Route
   */
  def serviceAndCompleteFn[T: ClassTag](
      fut: ⇒ Future[T]
  )(m: T ⇒ ToResponseMarshallable)(implicit fmt: Format[T]): Route =
    onComplete(fut) {
      case Success(value) ⇒ complete(m(value))
      case Failure(exception) ⇒
        Logger.error("Oops! Error while handling error ", exception)
        throw exception
      //        exception match {
      //          case e: BaseException ⇒ complete((StatusCodes.BadRequest.intValue, Error[String](error = e.getMessage)))
      //          case _: Throwable     ⇒ complete((StatusCodes.InternalServerError.intValue, Unexpected))
      //        }
    }

  /**
   * Uses service to get a result and then inspects that result to complete the request
   *
   * @return a completed Route
   */
  def serviceAndComplete[T: ClassTag](fut: ⇒ Future[T])(implicit fmt: Format[T]): Route =
    serviceAndCompleteFn(fut)(value ⇒ (StatusCodes.OK.intValue, Ok(response = Option(value))))
}
